#include "stdafx.h"
#include "Recorder.h"
#include "ListManager.h"
#include <cstdint>
#include <filesystem>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

CListManager ListManager;

CListManager::CListManager()
{
    shapeArray.RemoveAll();
    displayArray.RemoveAll();
    layoutArray.RemoveAll();
}

CListManager::~CListManager()
{
}

int CListManager::AddShapeArray(CTransparentWnd *newWnd)
{
    shapeArray.Add(newWnd);
    return 1;
}

// Note: The window is only detached from the array, it is not destroyed
int CListManager::RemoveShapeArray(CTransparentWnd *removeWnd, int wantDestroy)
{
    int found = 0;

    // \note lowering cast
    int max = static_cast<int>(shapeArray.GetSize());
    CTransparentWnd *itemWnd = nullptr;
    for (int i = 0; i < max; i++)
    {
        itemWnd = shapeArray[i];
        if (itemWnd == removeWnd)
        {
            shapeArray.RemoveAt(i, 1);
            found = 1;
            break; // allow only one removal
        }
    }

    if ((found) && (wantDestroy))
    {
        if (removeWnd)
        {
            // TODO ???
            // OnDestroy issue in cpp.
            // Output monitor reports: "OnDestroy or PostNcDestroy in derived class will not be called"
            // To destroy a C++ Windows object, use DestroyWindow, not "delete".
            // Ref: http://msdn.microsoft.com/en-us/library/5zba4hah%28VS.80%29.aspx
            // TRACE("## CListManager::RemoveShapeArray - DestroyWindow and delete.\n");

            removeWnd->DestroyWindow();
            delete removeWnd;
        }
    }

    return found;
}

int CListManager::LoadShapeArray(CString loadDir)
{
    return LoadShapeArray(loadDir, 1);
}

int CListManager::LoadShapeArray(CString loadDir, int freeExisting)
{
    int ret = TRUE;
    FILE *fptr = nullptr;
    _wfopen_s(&fptr, loadDir.GetString(), _T("rb"));
    if (fptr == nullptr)
    {
        // Silent Mode
        // MessageBox(nullptr,"Error opening shape file for loading","Note",MB_OK | MB_ICONEXCLAMATION);
        return FALSE;
    }
    long fileversion = 0;
    fread((void *)&fileversion, sizeof(long), 1, fptr);

    int max = 0;
    fread((void *)&max, sizeof(int), 1, fptr); // Number of Shapes

    int reserve[100];
    for (int j = 0; j < 100; j++)
        fread((void *)&reserve[j], sizeof(int), 1, fptr); // Reserve Bytes

    // Testing
    int failtest = 0;
    if (reserve[0] != 'c')
        failtest = 1;
    if (reserve[1] != 's')
        failtest = 1;
    if (reserve[2] != 'h')
        failtest = 1;
    if (reserve[3] != 'a')
        failtest = 1;
    if (reserve[4] != 'p')
        failtest = 1;
    if (reserve[5] != 'e')
        failtest = 1;

    if (failtest)
    {
        // MessageBox(nullptr,"Invalid shape file CamShapes.ini","Note",MB_OK | MB_ICONEXCLAMATION);
        MessageOut(nullptr, IDS_STRINGINSHAPEFILE, IDS_STRING_NOTE, MB_OK | MB_ICONEXCLAMATION);
        fclose(fptr);
        return FALSE;
    }

    if ((0 < max) && (max < 100000))
    {
        if (freeExisting)
        {
            FreeShapeArray();
        }
        for (int i = 0; i < max; i++)
        {
            // TODO, Possible memory leak, where is the delete operation of the new below done?
            CTransparentWnd *itemWnd = new CTransparentWnd;
            if (itemWnd)
            {
                if (!itemWnd->LoadShape(fptr))
                {
                    ret = FALSE;
                    break;
                }
                else
                {
                    CString pTitle(itemWnd->ShapeString());
                    itemWnd->CreateEx(WS_EX_TOPMOST, AfxRegisterWndClass(0), LPCTSTR(pTitle), WS_POPUP | WS_SYSMENU,
                                      itemWnd->RectWnd(), nullptr, 0, nullptr);

                    AddShapeArray(itemWnd);
                }
            }
        }
    }

    fclose(fptr);

    return ret;
}

int CListManager::SaveShapeArray(CString saveDir)
{
    int ret = TRUE;
    FILE *fptr = nullptr;

    const auto path = std::experimental::filesystem::path(saveDir.GetString());
    if (!std::experimental::filesystem::exists(path.parent_path()))
        std::experimental::filesystem::create_directories(path.parent_path());

    _wfopen_s(&fptr, saveDir.GetString(), _T("wb"));
    if (!fptr)
    {
        // MessageBox(nullptr,"Error opening shape file for saving","Note",MB_OK | MB_ICONEXCLAMATION);
        MessageOut(nullptr, IDS_STRING_EOSFS, IDS_STRING_NOTE, MB_OK | MB_ICONEXCLAMATION);
        return FALSE;
    }

    int32_t fileversion = 100;
    fwrite(&fileversion, sizeof(fileversion), 1, fptr);

    int max = static_cast<int>(shapeArray.GetSize());
    fwrite(&max, sizeof(max), 1, fptr); // Number of Shapes

    int reserve[100];
    reserve[0] = 'c';
    reserve[1] = 's';
    reserve[2] = 'h';
    reserve[3] = 'a';
    reserve[4] = 'p';
    reserve[5] = 'e';

    for (int j = 0; j < 100; j++)
        fwrite((void *)&reserve[j], sizeof(int), 1, fptr); // Reserve Bytes

    CTransparentWnd *itemWnd = nullptr;
    for (int i = 0; i < max; i++)
    {
        itemWnd = shapeArray[i];
        if (itemWnd)
        {
            if (!itemWnd->SaveShape(fptr))
            {
                ret = FALSE;
            }
        }
    }

    fclose(fptr);

    return ret;
}

int CListManager::AddDisplayArray(CTransparentWnd *newWnd)
{
    displayArray.Add(newWnd);
    return 1;
}

int CListManager::RemoveDisplayArray(CTransparentWnd *removeWnd, int wantDestroy)
{
    int found = 0;

    int max = displayArray.GetSize();
    CTransparentWnd *itemWnd = nullptr;
    for (int i = 0; i < max; i++)
    {
        itemWnd = displayArray[i];
        if (itemWnd == removeWnd)
        {
            displayArray.RemoveAt(i, 1);
            found = 1;
            break; // allow only one removal
        }
    }

    if ((found) && (wantDestroy))
    {
        if (removeWnd)
        {

            // TODO ???
            // OnDestroy issue in cpp.
            // Output monitor reports: "OnDestroy or PostNcDestroy in derived class will not be called"
            // To destroy a C++ Windows object, use DestroyWindow, not "delete".
            // Ref: http://msdn.microsoft.com/en-us/library/5zba4hah%28VS.80%29.aspx
            // TRACE("## CListManager::RemoveDisplayArray - DestroyWindow and delete.\n");

            removeWnd->DestroyWindow();
            delete removeWnd;
        }
    }

    return found;
}

int CListManager::LoadDisplayArray(CString loadDir)
{
    return 1;
}

int CListManager::SaveDisplayArray(CString saveDir)
{
    return 1;
}

int CListManager::AddLayoutArray(CLayoutList *pLayout)
{
    layoutArray.Add(pLayout);
    return 1;
}

int CListManager::RemoveLayoutArray(CLayoutList *pLayout, int wantDestroyLayout)
{
    int found = 0;

    int max = layoutArray.GetSize();
    CLayoutList *itemLayout = nullptr;
    for (int i = max - 1; i >= 0; i--)
    {
        itemLayout = layoutArray[i];
        if (itemLayout == pLayout)
        {
            layoutArray.RemoveAt(i, 1);
            found = 1;
            break; // allow only one removal
        }
    }

    if ((found) && (wantDestroyLayout))
    {
        if (itemLayout)
        {
            DestroyLayout(itemLayout);
        }
    }

    return found;
}

int CListManager::DestroyLayout(CLayoutList *pLayout)
{
    CArray<CTransparentWnd *, CTransparentWnd *> *removeArray = pLayout->layoutArrayPtr;

    DestroyArrayItems(removeArray);
    delete (pLayout->layoutArrayPtr);
    pLayout->layoutArrayPtr = nullptr;

    delete pLayout;

    return 1;
}

int CListManager::DestroyArrayItems(CArray<CTransparentWnd *, CTransparentWnd *> *removeArray)
{
    int max = removeArray->GetSize();
    CTransparentWnd *itemWnd = nullptr;
    for (int i = max - 1; 0 <= i; i--)
    {
        itemWnd = (*removeArray)[i];
        if (itemWnd)
        {
            if (itemWnd->TrackingOn() || itemWnd->EditTransOn() || itemWnd->EditImageOn())
            {
                // Do not remove, the item is undergoing editing
            }
            else
            {
                itemWnd->ShowWindow(SW_HIDE);

                // need to clone the display array
                removeArray->RemoveAt(i, 1);
                itemWnd->DestroyWindow();
                delete itemWnd;
            }
        }
    }

    return 1;
}

CArray<CTransparentWnd *, CTransparentWnd *> *CListManager::CloneDisplayArray()
{
    // TODO, Possible memory leak, where is the delete operation of the new below done?
    CArray<CTransparentWnd *, CTransparentWnd *> *cloneArray = new CArray<CTransparentWnd *, CTransparentWnd *>;

    int max = displayArray.GetSize();
    CTransparentWnd *itemWnd = nullptr;
    for (int i = 0; i < max; i++)
    {
        itemWnd = displayArray[i];
        if (itemWnd)
        {
            // need to clone the display array
            CTransparentWnd *newWnd = itemWnd->Clone(0, 0);
            if (newWnd)
                cloneArray->Add(newWnd);
        }
    }

    return cloneArray;
}

int CListManager::FreeShapeArray()
{
    DestroyArrayItems(&shapeArray);
    return 1;
}

int CListManager::FreeDisplayArray()
{
    DestroyArrayItems(&displayArray);
    return 1;
}

int CListManager::FreeLayoutArray()
{
    // Free Multiple Lists
    int max = layoutArray.GetSize();
    for (int i = max - 1; i >= 0; i--)
    {
        CLayoutList *pLayout = layoutArray[i];
        if (pLayout)
        {
            RemoveLayoutArray(pLayout, 1);
        }
    }

    return 1;
}

CArray<CTransparentWnd *, CTransparentWnd *> *CListManager::CloneLayoutArrayPtr(CLayoutList *itemLayout)
{
    CArray<CTransparentWnd *, CTransparentWnd *> *layoutArrayPtr = itemLayout->layoutArrayPtr;
    // TODO, Possible memory leak, where is the delete operation of the new below done?
    CArray<CTransparentWnd *, CTransparentWnd *> *cloneArray = new CArray<CTransparentWnd *, CTransparentWnd *>;

    int max = layoutArrayPtr->GetSize();
    for (int i = 0; i < max; i++)
    {
        CTransparentWnd *itemWnd = (*layoutArrayPtr)[i];
        if (itemWnd)
        {
            // need to clone the display array
            CTransparentWnd *newWnd = itemWnd->Clone(0, 0);
            if (newWnd)
            {
                cloneArray->Add(newWnd);
            }
        }
    }

    return cloneArray;
}

CLayoutList *CListManager::CloneLayout(CLayoutList *itemLayout)
{
    CLayoutList *newLayout = nullptr;
    if (itemLayout)
    {
        CArray<CTransparentWnd *, CTransparentWnd *> *cloneArray = CloneLayoutArrayPtr(itemLayout);
        if (cloneArray)
        {
            // TODO, Possible memory leak, where is the delete operation of the new below done?
            newLayout = new CLayoutList;
            if (newLayout)
            {
                newLayout->layoutArrayPtr = cloneArray;
                newLayout->layoutName = itemLayout->layoutName;
            }
        }
    }

    return newLayout;
}

int CListManager::SwapShapeArray(long uniqueID1, long uniqueID2)
{
    CTransparentWnd *tempWnd = nullptr;
    int swapItem1 = -1;
    int swapItem2 = -1;

    int max = shapeArray.GetSize();
    for (int i = 0; i < max; i++)
    {
        CTransparentWnd *itemWnd = shapeArray[i];
        if (itemWnd)
        {
            if (itemWnd->UniqueID() == uniqueID1)
            {
                swapItem1 = i;
            }
            else if (itemWnd->UniqueID() == uniqueID2)
            {
                swapItem2 = i;
            }
        }
    }

    // Perform the swap
    if ((swapItem1 > -1) && (swapItem2 > -1))
    {
        tempWnd = shapeArray[swapItem1];
        shapeArray[swapItem1] = shapeArray[swapItem2];
        shapeArray[swapItem2] = tempWnd;
        return 1;
    }

    return 0;
}

int CListManager::SwapLayoutArray(long uniqueID1, long uniqueID2)
{
    CLayoutList *tempLayout = nullptr;
    int swapItem1 = -1;
    int swapItem2 = -1;

    int max = layoutArray.GetSize();
    CLayoutList *itemLayout = nullptr;
    for (int i = 0; i < max; i++)
    {
        itemLayout = layoutArray[i];
        if (itemLayout)
        {
            if (itemLayout->uniqueID == uniqueID1)
            {
                swapItem1 = i;
            }
            else if (itemLayout->uniqueID == uniqueID2)
            {
                swapItem2 = i;
            }
        }
    }

    // Perform the swap
    if ((swapItem1 > -1) && (swapItem2 > -1))
    {
        tempLayout = layoutArray[swapItem1];
        layoutArray[swapItem1] = layoutArray[swapItem2];
        layoutArray[swapItem2] = tempLayout;
        return 1;
    }

    return 0;
}

int CListManager::LoadLayout(CString loadDir)
{
    int ret = TRUE;
    FILE *fptr = nullptr;
    _wfopen_s(&fptr, loadDir.GetString(), _T("rb"));
    if (fptr != nullptr)
    {
        long fileversion = 0;
        fread((void *)&fileversion, sizeof(long), 1, fptr);

        int max = 0;
        fread((void *)&max, sizeof(int), 1, fptr); // Number of Layouts

        int reserve[100];
        for (int j = 0; j < 100; j++)
            fread((void *)&reserve[j], sizeof(int), 1, fptr); // Reserve Bytes

        // Testing
        int failtest = 0;
        if (reserve[0] != 'c')
            failtest = 1;
        if (reserve[1] != 'l')
            failtest = 1;
        if (reserve[2] != 'a')
            failtest = 1;
        if (reserve[3] != 'y')
            failtest = 1;
        if (reserve[4] != 'o')
            failtest = 1;
        if (reserve[5] != 'u')
            failtest = 1;
        if (reserve[6] != 't')
            failtest = 1;

        if (failtest)
        {
            // MessageBox(nullptr,"Invalid layout file CamLayout.ini","Note",MB_OK | MB_ICONEXCLAMATION);
            MessageOut(nullptr, IDS_STRINGINVLAYOUTFILE, IDS_STRING_NOTE, MB_OK | MB_ICONEXCLAMATION);

            fclose(fptr);
            return FALSE;
        }

        if ((max > 0) && (max < 100000))
        {
            FreeLayoutArray();
            for (int i = 0; i < max; i++)
            {
                // TODO, Possible memory leak, where is the delete operation of the new below done?
                CLayoutList *itemLayout = new CLayoutList;
                if (itemLayout)
                {
                    if (!itemLayout->LoadLayoutFromFile(fptr))
                    {
                        ret = FALSE;
                        break;
                    }

                    AddLayoutArray(itemLayout);
                }
            }
        }

        fclose(fptr);
    }
    else
    {
        // Silent Mode
        // MessageBox(nullptr,"Error opening layout file for loading","Note",MB_OK | MB_ICONEXCLAMATION);
        return FALSE;
    }

    return ret;
}

int CListManager::SaveLayout(CString saveDir)
{
    int ret = TRUE;
    FILE *fptr = nullptr;
    _wfopen_s(&fptr, saveDir.GetString(), _T("wb"));
    if (fptr != nullptr)
    {
        long fileversion = 100;
        fwrite((void *)&fileversion, sizeof(long), 1, fptr);

        int max = layoutArray.GetSize();
        fwrite((void *)&max, sizeof(int), 1, fptr); // Number of Shapes

        int reserve[100];
        reserve[0] = 'c';
        reserve[1] = 'l';
        reserve[2] = 'a';
        reserve[3] = 'y';
        reserve[4] = 'o';
        reserve[5] = 'u';
        reserve[6] = 't';

        for (int j = 0; j < 100; j++)
            fwrite((void *)&reserve[j], sizeof(int), 1, fptr); // Reserve Bytes

        CLayoutList *itemLayout = nullptr;
        for (int i = 0; i < max; i++)
        {
            itemLayout = layoutArray[i];
            if (itemLayout)
            {
                if (!itemLayout->SaveLayoutToFile(fptr))
                {
                    ret = FALSE;
                }
            }
        }

        fclose(fptr);
    }
    else
    {
        // MessageBox(nullptr,"Error opening layout file for saving","Note",MB_OK | MB_ICONEXCLAMATION);
        MessageOut(nullptr, IDS_STRING_EOLFS, IDS_STRING_NOTE, MB_OK | MB_ICONEXCLAMATION);
    }

    return ret;
}

int CListManager::SaveLayoutArrayToFile(CArray<CTransparentWnd *, CTransparentWnd *> *layoutArrayPtr, FILE *fptr)
{
    int ret = TRUE;
    long shapearrayversion = 100;
    fwrite((void *)&shapearrayversion, sizeof(long), 1, fptr);

    // \note lowering cast
    int max = static_cast<int>(layoutArrayPtr->GetSize());
    fwrite((void *)&max, sizeof(int), 1, fptr); // Number of Shapes

    CTransparentWnd *itemWnd = nullptr;
    for (int i = 0; i < max; i++)
    {
        itemWnd = (*layoutArrayPtr)[i];
        if (itemWnd)
        {
            if (!itemWnd->SaveShape(fptr))
            {
                ret = FALSE;
            }
        }
    }

    return ret;
}

// Ensure window is on the top of display list
void CListManager::EnsureOnTopList(CTransparentWnd *transWnd)
{
    int max = displayArray.GetSize();
    if (max <= 0)
        return;
    if (transWnd == displayArray[max - 1])
    {
        // at bottom of list (i.e on top..no change)
        // no change
        return;
    }
    int found = RemoveDisplayArray(transWnd, 0);
    if (found)
    {
        AddDisplayArray(transWnd);
    }

    return;
}

int CListManager::LoadLayoutArrayFromFile(CArray<CTransparentWnd *, CTransparentWnd *> *layoutArrayPtr, FILE *fptr)
{
    int ret = TRUE;

    if (layoutArrayPtr == nullptr)
        return FALSE;

    long layoutarrayversion = 0;
    fread((void *)&layoutarrayversion, sizeof(long), 1, fptr);

    int max = 0;
    fread((void *)&max, sizeof(int), 1, fptr); // Number of Shapes

    if ((max > 0) && (max < 10000))
    {
        for (int i = 0; i < max; i++)
        {
            // TODO, Possible memory leak, where is the delete operation of the new below done?
            CTransparentWnd *itemWnd = new CTransparentWnd;
            if (itemWnd)
            {
                if (!itemWnd->LoadShape(fptr))
                {
                    ret = FALSE;
                    break;
                }
                else
                {
                    CString pTitle(itemWnd->ShapeString());
                    itemWnd->CreateEx(WS_EX_TOPMOST, AfxRegisterWndClass(0), LPCTSTR(pTitle), WS_POPUP | WS_SYSMENU,
                                      itemWnd->RectWnd(), nullptr, 0, nullptr);

                    if (layoutArrayPtr)
                    {
                        layoutArrayPtr->Add(itemWnd);
                    }
                }
            }
        }
    }

    // Future Enhancements
    if (layoutarrayversion > 100)
    {
    }

    return ret;
}